package com.ruoyi.project.platform.service.impl;

import java.util.*;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.exception.Exceptions;
import com.ruoyi.common.exception.MyAssert;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.aspectj.lang.annotation.DataScope;
import com.ruoyi.project.platform.agent.ContainerAgent;
import com.ruoyi.project.platform.agent.basic.AccountDto;
import com.ruoyi.project.platform.agent.basic.Credential;
import com.ruoyi.project.platform.agent.basic.RestClientWrapper;
import com.ruoyi.project.platform.agent.basic.TriggerDto;
import com.ruoyi.project.platform.agent.constant.Platform;
import com.ruoyi.project.platform.domain.*;
import com.ruoyi.project.platform.dto.RunningContainerDto;
import com.ruoyi.project.platform.dto.UserRunningDto;
import com.ruoyi.project.platform.mapper.*;
import com.ruoyi.project.system.domain.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.project.platform.service.IRRobotRunningService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import static com.ruoyi.common.exception.Exceptions.NOT_FOUND_RUNNING;

/**
 * 机器人实例Service业务层处理
 *
 * @author ruoyi
 * @date 2020-07-29
 */
@Service
public class RRobotRunningServiceImpl implements IRRobotRunningService {
    @Autowired
    private RRobotRunningMapper rRobotRunningMapper;

    @Autowired
    private PltUserOrderDetailMapper pltUserOrderDetailMapper;

    @Autowired
    private ContainerAgent containerAgent;

    @Autowired
    private StyStrategyMapper styStrategyMapper;

    @Autowired
    RRunningExchangeMapper rRunningExchangeMapper;

    @Autowired
    RRunningMappingMapper rRunningMappingMapper;

    @Autowired
    RestClientWrapper rootAgent;


    /**
     * 查询机器人实例
     *
     * @param runningId 机器人实例ID
     * @return 机器人实例
     */
    @Override
    public RRobotRunning selectRRobotRunningById(Long runningId) {
        return rRobotRunningMapper.selectRRobotRunningById(runningId);
    }

    /**
     * 查询机器人实例列表
     *
     * @param rRobotRunning 机器人实例
     * @return 机器人实例
     */
    @Override
    @DataScope(userAlias = "u")
    public List<RRobotRunning> selectRRobotRunningList(RRobotRunning rRobotRunning) {
        return rRobotRunningMapper.selectRRobotRunningList(rRobotRunning);
    }

    /**
     * 新增机器人实例
     *
     * @param rRobotRunning 机器人实例
     * @return 结果
     */
    @Override
    public int insertRRobotRunning(RRobotRunning rRobotRunning) {
        SysUser user = SecurityUtils.getLoginUser().getUser();
        if (!user.isAdmin()) {
            rRobotRunning.setUserId(user.getUserId());
            rRobotRunning.setUserName(user.getUserName());
        }

        rRobotRunning.setPltKey(UUID.randomUUID().toString());
        rRobotRunning.setPltSecret(UUID.randomUUID().toString());
        return rRobotRunningMapper.insertRRobotRunning(rRobotRunning);
    }

    /**
     * 修改机器人实例
     *
     * @param rRobotRunning 机器人实例
     * @return 结果
     */
    @Override
    public int updateRRobotRunning(RRobotRunning rRobotRunning) {
        return rRobotRunningMapper.updateRRobotRunning(rRobotRunning);
    }

    /**
     * 批量删除机器人实例
     *
     * @param runningIds 需要删除的机器人实例ID
     * @return 结果
     */
    @Override
    public int deleteRRobotRunningByIds(Long[] runningIds) {
        return rRobotRunningMapper.deleteRRobotRunningByIds(runningIds);
    }

    /**
     * 删除机器人实例信息
     *
     * @param runningId 机器人实例ID
     * @return 结果
     */
    @Override
    public int deleteRRobotRunningById(Long runningId) {
        return rRobotRunningMapper.deleteRRobotRunningById(runningId);
    }

    @Override
    public UserRunningDto.UserResource resourcesByUserId(Long userId) {//TODO:订单项状态过滤
        UserRunningDto.UserResource urd = new UserRunningDto.UserResource();
        PltUserOrderDetail purd = new PltUserOrderDetail();
        purd.setUserId(userId);
        List<PltUserOrderDetail> results = pltUserOrderDetailMapper.selectPltUserOrderDetailList(purd);
        Map<String, List<PltUserOrderDetail>> temp = new HashMap<>();
        if (!results.isEmpty()) {
            results.forEach(m -> {
                if (m.getServiceStatus().equals("actived")) {
                    if (m.getServiceCategory().equals("strategy")) {
                        UserRunningDto.StrategyResourceItem resourceItem = new UserRunningDto.StrategyResourceItem();
                        resourceItem.setStrategyId(m.getServiceId());
                        resourceItem.setName(m.getServiceName());
                        resourceItem.setType(Integer.parseInt(m.getServiceType()));
                        urd.getStrategies().add(resourceItem);
                    } else if (m.getServiceCategory().equals("robot")) {
                        if (!temp.containsKey(m.getServiceType())) {
                            temp.put(m.getServiceType(), new ArrayList<>());
                        }
                        temp.get(m.getServiceType()).add(m);
                    } else {
                        throw new RuntimeException("not support");
                    }
                }
            });
            if (temp.size() > 0) {
                List<PltUserOrderDetail> list = temp.get("3");
                if (list != null && !list.isEmpty()) {
                    UserRunningDto.RobotResourceItem resourceItem = new UserRunningDto.RobotResourceItem();
                    resourceItem.setName("黄金机器人");
                    resourceItem.setType(3);
                    resourceItem.setCanCreate(list.size());
                    urd.setRobotScore(urd.getRobotScore() + 4 * list.size());
                    urd.getRobots().add(resourceItem);
                }

                list = temp.get("2");
                if (list != null && !list.isEmpty()) {
                    UserRunningDto.RobotResourceItem resourceItem = new UserRunningDto.RobotResourceItem();
                    resourceItem.setName("白银机器人");
                    resourceItem.setType(2);
                    resourceItem.setCanCreate(list.size());
                    urd.setRobotScore(urd.getRobotScore() + 2 * list.size());
                    urd.getRobots().add(resourceItem);
                }

                list = temp.get("1");
                if (list != null && !list.isEmpty()) {
                    UserRunningDto.RobotResourceItem resourceItem = new UserRunningDto.RobotResourceItem();
                    resourceItem.setName("普通机器人");
                    resourceItem.setType(1);
                    resourceItem.setCanCreate(list.size());
                    urd.setRobotScore(urd.getRobotScore() + 1 * list.size());
                    urd.getRobots().add(resourceItem);
                }
                urd.getRobots().stream().sorted(Comparator.comparingInt(UserRunningDto.RobotResourceItem::getType));
                urd.getStrategies().stream().sorted(Comparator.comparingInt(UserRunningDto.StrategyResourceItem::getType));
            }

        }

        return urd;
    }

    @Override
    public Object create(Long endpointId, Long robotId) {
        RRobotRunning running = rRobotRunningMapper.selectRRobotRunningById(robotId);
        running.getUserId();
        if (StringUtils.isEmpty(running.getContainerId())) {
            RunningContainerDto.ContainerCreatedBase base = new RunningContainerDto.ContainerCreatedBase();
            StyStrategy strategy = styStrategyMapper.selectStyStrategyById(running.getStrategyId());
            MyAssert.assertNotEmpty(strategy.getImage(), Exceptions.BAD_IMAGE_NAME);
            MyAssert.assertNotEmpty(strategy.getTagVersion(), Exceptions.BAD_IMAGE_TAG);
            String imageTag = strategy.getImage() + ":" + strategy.getTagVersion();
            base.setImage(imageTag);
            Map<String, String> lables = new HashMap<>();
            lables.put("userId", running.getUserId() + "");
            lables.put("userName", running.getUserName());
            base.setLabels(lables);
            Object o = containerAgent.create(endpointId, base);
            JSONObject jo = (JSONObject) o;
            running.setImageTag(imageTag);
            String contianerId = jo.getString("Id");
            if (!StringUtils.isEmpty(contianerId)) {
                running.setContainerId(contianerId.substring(0, 12));
            }

            running.setRunningVersion(strategy.getTagVersion());
            running.setEndpointId(endpointId + "");
            rRobotRunningMapper.updateRRobotRunning(running);
        }

        syncStates(running.getRunningId());
        return running;
    }

    @Override
    public Object stop(Long runningId) {
        RRobotRunning running = rRobotRunningMapper.selectRRobotRunningById(runningId);
        MyAssert.assertNotNull(running, NOT_FOUND_RUNNING);
        MyAssert.assertNotEmpty(running.getContainerId(), Exceptions.NOT_FOUND_CONTAINER);
        MyAssert.assertNotEmpty(running.getEndpointId(), Exceptions.NOT_FOUND_ENDPOINT);

        Object result = containerAgent.stop(Long.parseLong(running.getEndpointId()), running.getContainerId());
        syncStates(runningId);
        return result;
    }

    @Override
    public Object start(Long runningId) {
        RRobotRunning running = rRobotRunningMapper.selectRRobotRunningById(runningId);
        MyAssert.assertNotNull(running, NOT_FOUND_RUNNING);
        MyAssert.assertNotEmpty(running.getContainerId(), Exceptions.NOT_FOUND_CONTAINER);
        MyAssert.assertNotEmpty(running.getEndpointId(), Exceptions.NOT_FOUND_ENDPOINT);

        Object result = containerAgent.start(Long.parseLong(running.getEndpointId()), running.getContainerId());
        syncStates(runningId);
        return result;
    }

    @Override
    public Object remove(Long runningId) {
        RRobotRunning running = rRobotRunningMapper.selectRRobotRunningById(runningId);
        MyAssert.assertNotNull(running, NOT_FOUND_RUNNING);
        MyAssert.assertNotEmpty(running.getContainerId(), Exceptions.NOT_FOUND_CONTAINER);
        MyAssert.assertNotEmpty(running.getEndpointId(), Exceptions.NOT_FOUND_ENDPOINT);

        Object delResult = containerAgent.del(Long.parseLong(running.getEndpointId()), running.getContainerId());

        rRobotRunningMapper.deleteRobootStatus(runningId);
        return delResult;
    }

    @Override
    public Object restart(Long runningId) {
        RRobotRunning running = rRobotRunningMapper.selectRRobotRunningById(runningId);
        MyAssert.assertNotNull(running, NOT_FOUND_RUNNING);
        MyAssert.assertNotEmpty(running.getContainerId(), Exceptions.NOT_FOUND_CONTAINER);
        MyAssert.assertNotEmpty(running.getEndpointId(), Exceptions.NOT_FOUND_ENDPOINT);

        Object result = containerAgent.restart(Long.parseLong(running.getEndpointId()), running.getContainerId());
        syncStates(runningId);
        return result;
    }

    @Override
    public Object pause(Long runningId) {
        RRobotRunning running = rRobotRunningMapper.selectRRobotRunningById(runningId);
        MyAssert.assertNotNull(running, NOT_FOUND_RUNNING);
        MyAssert.assertNotEmpty(running.getContainerId(), Exceptions.NOT_FOUND_CONTAINER);
        MyAssert.assertNotEmpty(running.getEndpointId(), Exceptions.NOT_FOUND_ENDPOINT);

        Object result = containerAgent.pause(Long.parseLong(running.getEndpointId()), running.getContainerId());
        syncStates(runningId);
        return result;
    }

    @Override
    public Object reverse(Long runningId) {
        RRobotRunning running = rRobotRunningMapper.selectRRobotRunningById(runningId);
        MyAssert.assertNotNull(running, NOT_FOUND_RUNNING);
        MyAssert.assertNotEmpty(running.getContainerId(), Exceptions.NOT_FOUND_CONTAINER);
        MyAssert.assertNotEmpty(running.getEndpointId(), Exceptions.NOT_FOUND_ENDPOINT);

        Object result = containerAgent.unpause(Long.parseLong(running.getEndpointId()), running.getContainerId());
        syncStates(runningId);
        return result;
    }

    @Override
    public List<UserRunningDto.ExchangeDto> exchanges(String containerId) {
        RRobotRunning condition = new RRobotRunning();
        condition.setContainerId(containerId);
        return rRobotRunningMapper.exchanges(containerId);
    }

    @Override
    public List<UserRunningDto.MappingDto> mappings(String containerId) {
        RRobotRunning condition = new RRobotRunning();
        condition.setContainerId(containerId);
        return rRobotRunningMapper.mappings(containerId);
    }

    @Override
    public Object inspect(Long runningId) {
        RRobotRunning running = rRobotRunningMapper.selectRRobotRunningById(runningId);
        MyAssert.assertNotNull(running, NOT_FOUND_RUNNING);
        MyAssert.assertNotEmpty(running.getContainerId(), Exceptions.NOT_FOUND_CONTAINER);
        MyAssert.assertNotEmpty(running.getEndpointId(), Exceptions.NOT_FOUND_ENDPOINT);
        Object inspect = containerAgent.inspect(Long.parseLong(running.getEndpointId()), running.getContainerId());
        JSONObject jo = (JSONObject) inspect;
        Object state = jo.getJSONObject("State");
        RRobotRunning toUpdate = new RRobotRunning();
        toUpdate.setRunningId(running.getRunningId());
        toUpdate.setState(JSON.toJSONString(state));
        rRobotRunningMapper.updateRRobotRunning(toUpdate);
        return rRobotRunningMapper.selectRRobotRunningById(runningId);
    }

    @Override
    public void syncStates() {
        List<RRobotRunning> runnings = rRobotRunningMapper.selectAll();
        if (!runnings.isEmpty()) runnings.forEach(m -> {
            try {
                if (!StringUtils.isEmpty(m.getContainerId())) {
                    Object obj = containerAgent.inspect(Long.parseLong(m.getEndpointId()), m.getContainerId());
                    JSONObject jo = (JSONObject) obj;
                    Object state = jo.getJSONObject("State");
                    RRobotRunning toUpdate = new RRobotRunning();
                    toUpdate.setRunningId(m.getRunningId());
                    toUpdate.setState(JSON.toJSONString(state));
                    rRobotRunningMapper.updateRRobotRunning(toUpdate);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    @Override
    public Object updateVersion(Long runningId) {
        RRobotRunning running = rRobotRunningMapper.selectRRobotRunningById(runningId);
        StyStrategy styStrategy = styStrategyMapper.selectStyStrategyById(running.getStrategyId());
        if (!styStrategy.getTagVersion().equals(running.getRunningVersion())) {
            running.setRunningVersion(styStrategy.getTagVersion());
            running.setImageTag(styStrategy.getImage() + ":" + styStrategy.getTagVersion());
            rRobotRunningMapper.updateRRobotRunning(running);
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object verify(RunningContainerDto.VerifyBase verify) {
        MyAssert.assertNotEmpty(verify.getPltKey(), Exceptions.BAD_REQUEST_PARAMETER_ERROR);
        MyAssert.assertNotEmpty(verify.getPltKey(), Exceptions.BAD_REQUEST_PARAMETER_ERROR);
        MyAssert.assertNotEmpty(verify.getRobotUrl(), Exceptions.BAD_REQUEST_PARAMETER_ERROR);
        RRobotRunning running = new RRobotRunning().setPltKey(verify.getPltKey()).setPltSecret(verify.getPltSecret());
        RRobotRunning result = rRobotRunningMapper.selectRRobotRunningList(running).stream().findAny().orElseThrow(
                () -> NOT_FOUND_RUNNING);

        result.setRobotUrl(verify.getRobotUrl());
        rRobotRunningMapper.updateRRobotRunning(result);
        return result;
    }

    @Override
    public Object getCurrentBalance(String runningId, Platform platform) {
        return rootAgent.getCurrentBalance(getCredentialByRunning(runningId), platform).getData();
    }

    @Override
    public Object getAccountReport(String runningId, Platform platform) {
        return rootAgent.report(getCredentialByRunning(runningId), platform).getData();
    }

    @Override
    public Object getAvgDealPrice(String runningId, Platform platform, Long start) {
        return rootAgent.avgPrice(getCredentialByRunning(runningId), platform, start).getData();
    }

    @Override
    public Object analyseDepths(String runningId, Platform platform) {
        return rootAgent.depthOrder(getCredentialByRunning(runningId), platform).getData();
    }

    @Override
    public Object getTicker(String runningId, Platform platform) {
        return rootAgent.getTicker(getCredentialByRunning(runningId), platform).getData();
    }

    @Override
    public Object queryFlyOrders(String runningId, Platform platform, Integer type) {
        return rootAgent.queryFlyOrders(getCredentialByRunning(runningId), platform, type).getData();
    }

    @Override
    public Object getDealedOrder(String runningId, Platform platform, Integer type, Long start, Long end, Long size, Long page) {
        return rootAgent.getDealedOrder(getCredentialByRunning(runningId), platform, type, start, end, size, page).getData();
    }

    @Override
    public Object placeOrder(String runningId, AccountDto.FixedAction action) {
        return rootAgent.placeOrder(getCredentialByRunning(runningId), action).getData();
    }

    @Override
    public Object cancelOrder(String runningId, String orderId, Platform platform, Integer type) {
        return rootAgent.cancelOrder(getCredentialByRunning(runningId), platform, type, orderId).getData();
    }

    @Override
    public Object caclasSafeStop(String runningId, Platform platform, Integer delay) {
        return rootAgent.safeStop(getCredentialByRunning(runningId), platform, delay).getData();
    }

    @Override
    public Object configAction(String runningId, AccountDto.Action action) {
        return rootAgent.configAction(getCredentialByRunning(runningId), action).getData();
    }

    @Override
    public List<RRobotRunning> getRuningByIds(Long[] runningIds) {
        return rRobotRunningMapper.selectRRobotRunningListByIds(runningIds);
    }

    @Override
    public Object usdtCurrent(String runningId, Platform platform, Long start) {
        return rootAgent.usdtTotal(getCredentialByRunning(runningId), platform, start).getData();
    }

    @Override
    public Object createTrigger(String runningId, TriggerDto.TriggerBase trigger) {
        return rootAgent.createTrigger(getCredentialByRunning(runningId), trigger).getData();
    }

    @Override
    public Object listTrigger(String runningId) {
        return rootAgent.listTrigger(getCredentialByRunning(runningId)).getData();
    }

    @Override
    public Object closeTrigger(String runningId, Long id) {
        return rootAgent.close(getCredentialByRunning(runningId), id).getData();
    }

    @Override
    public Object activeTrigger(String runningId, Long id) {
        return rootAgent.active(getCredentialByRunning(runningId), id).getData();
    }

    @Override
    public Object freshTrigger(String runningId, Long id) {
        return rootAgent.fresh(getCredentialByRunning(runningId), id).getData();
    }

    @Override
    public Object deleteTrigger(String runningId, Long id) {
        return rootAgent.delete(getCredentialByRunning(runningId), id).getData();
    }

    @Override
    public Object closeAll(String runningId) {
        return rootAgent.closeAll(getCredentialByRunning(runningId)).getData();
    }

    @Override
    public Object freshAll(String runningId) {
        return rootAgent.freshAll(getCredentialByRunning(runningId)).getData();
    }

    @Override
    public Object activeAll(String runningId) {
        return rootAgent.activeAll(getCredentialByRunning(runningId)).getData();
    }

    private Credential getCredentialByRunning(String runningId) {
        RRobotRunning result = rRobotRunningMapper.selectRRobotRunningById(Long.parseLong(runningId));
        MyAssert.assertNotNull(result, NOT_FOUND_RUNNING);
        return new Credential().applyTo(result);
    }

    public void syncStates(Long runningId) {
        RRobotRunning running = rRobotRunningMapper.selectRRobotRunningById(runningId);
        try {
            if (!StringUtils.isEmpty(running.getContainerId())) {
                Object obj = containerAgent.inspect(Long.parseLong(running.getEndpointId()), running.getContainerId());
                JSONObject jo = (JSONObject) obj;
                Object state = jo.getJSONObject("State");
                RRobotRunning toUpdate = new RRobotRunning();
                toUpdate.setRunningId(running.getRunningId());
                toUpdate.setState(JSON.toJSONString(state));
                rRobotRunningMapper.updateRRobotRunning(toUpdate);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
